/*
 * $Id: ShellApp.cc,v 1.13 2006-02-06 14:49:54 bacon Exp $
 */

#include "Globals.h"
#include "ShellApp.h"
#include "MainApp.h"

#include <xp/bas/sysapi.h>
#include <xp/bas/stdlib.h>
#include <xpp/bas/MutexLock.h>

ShellApp::ShellApp (): parent_pid(0), nanny_pid(0), child_pid(0)
{
	setup_signal_handlers ();
}

ShellApp::~ShellApp () 
{
	restore_signal_handlers ();
}
    
int ShellApp::run ()
{
	stop_requested = false;

#ifdef _WIN32
	return run_main_task ();
#else
	parent_pid = xp_getpid();

	while (!stop_requested) {
		int status;

		nanny_pid = xp_fork ();
		if (nanny_pid == -1) return -1;
		if (nanny_pid == 0) return run_nanny ();

		for (;;) {
			xp_pid_t wpid = xp_wait (nanny_pid, &status, 0);
			if (wpid == -1) return -1;
			if (wpid == nanny_pid) break;
		}
		if (XP_WIFEXITED(status) && XP_WEXITCODE(status) == 0) break;

		xp_sleep (1000);
	}
#endif

	return 0;
}

#ifndef _WIN32
int ShellApp::run_nanny ()
{
	// nanny_pid would be still 0 without this in the nanny itself
	nanny_pid = xp_getpid ();  
	xp_setpgid (nanny_pid, 0);

	child_pid = xp_fork ();
	if (child_pid == -1) return -1;
	if (child_pid == 0) {
		try { 
			//return run_main_task ();
			xp_exit (run_main_task ());
		}
		catch (...) { return -1; }
	}

	int status;
	for (;;) {
		xp_pid_t wpid = xp_wait (child_pid, &status, 0);
		if (wpid == -1) return -1;
		if (wpid == child_pid) break;
	}
	if (XP_WIFEXITED(status) && XP_WEXITCODE(status) == 0) return 0;

	xp_kill (0, SIGKILL); // xp_kill all processes in my process group
	return -1;
}
#endif

int ShellApp::run_main_task ()
{
#ifndef _WIN32
	child_pid = xp_getpid ();  // just in case.
#endif

	load_ini_file ();
	if (app_mode == APP_MODE_FOREGROUND) {
		xp_log_settarget (&app_log,
			xp_log_gettarget(&app_log) | XP_LOG_CONSOLE);
	}

	MainApp app;
	{
		MutexLock lock (&app_main_mutex);
		app_main = &app;
	}

	int ret_code;
	if (app.run() == -1) {
		XP_LOG0 (&app_log, APP_NAME, XP_LOG_CRITICAL,
			XP_TEXT("failed to launch the main application"));
		ret_code = -1;
	}	
	else ret_code = app.returnCode();

	{
		MutexLock lock (&app_main_mutex);
		app_main = XP_NULL;
	}

	return ret_code;
}

int ShellApp::stop ()
{
	stop_requested = true;
	MutexLock lock (&app_main_mutex);
	if (app_main == XP_NULL) return -1;
	return app_main->stop ();
}

int ShellApp::shutdown ()
{
	stop_requested = true;
	MutexLock lock (&app_main_mutex);
	if (app_main == XP_NULL) return -1;
	return app_main->stop ();
}

void ShellApp::handleSignal (int sig)
{
#ifdef _WIN32
	/*return*/ this->stop ();
#else
	xp_pid_t pid = xp_getpid();

	if (pid == parent_pid) {
		xp_kill (nanny_pid, sig);
		//return 0;
	}
	else if (pid == nanny_pid) {
		xp_kill (child_pid, sig);
		//return 0;
	}
	else /*return*/ this->stop ();
#endif
}

void ShellApp::setup_signal_handlers ()
{
	// ignoreSignal(32) caused the system to freeze on Linux
	//ignoreAllSignals (); 
	
	//ignoreSignal (SIGCHLD);
#ifdef SIGPIPE
	ignoreSignal (SIGPIPE);
#endif

#ifdef SIGHUP
	manageSignal (SIGHUP);
#endif
#ifdef SIGINT
	manageSignal (SIGINT);
#endif
#ifdef SIGQUIT
	manageSignal (SIGQUIT);
#endif
#ifdef SIGTERM
	manageSignal (SIGTERM);
#endif
}

void ShellApp::restore_signal_handlers ()
{
	//defaultifyAllSignals ();

#ifdef SIGCHLD
	defaultifySignal (SIGCHLD);
#endif
#ifdef SIGPIPE
	defaultifySignal (SIGPIPE);
#endif
#ifdef SIGHUP
	defaultifySignal (SIGHUP);
#endif
#ifdef SIGINT
	defaultifySignal (SIGINT);
#endif
#ifdef SIGQUIT
	defaultifySignal (SIGQUIT);
#endif
#ifdef SIGTERM
	defaultifySignal (SIGTERM);
#endif
}

void ShellApp::load_ini_file ()
{
	app_env_path.chmod (APP_FILE_PERM);

	app_env.preLoad ();
	if (app_env_path.read (&app_env) == -1) {
		XP_LOG1 (&app_log, APP_NAME, XP_LOG_INFO,
			XP_TEXT("cannot load %s. using all default values"), 
			app_env_path.name());
	}
	app_env.postLoad ();
}
